package com.briup.sm2;

import com.briup.utils.URLCodeUtil;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.SM2Signer;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import java.security.KeyPair;


public class SM2Util {

    private final static Digest DIGEST = new SM3Digest();

    /**
     * 加密
     * @param data      数据
     * @param publicKey 公钥
     * @return 加密之后的数据
     */
    public static byte[] encrypt(byte[] data, byte[] publicKey) throws Exception {
        CipherParameters pubKeyParameters = new ParametersWithRandom(SM2.publicKeyToParams("SM2", publicKey));
        SM2Engine engine = new SM2Engine(DIGEST, SM2Engine.Mode.C1C3C2);
        engine.init(true, pubKeyParameters);
        return engine.processBlock(data, 0, data.length);
    }

    /**
     * 公钥加密
     * @param plaintext
     * @param publicKeyStr
     * @return
     * @throws Exception
     */
    public static String encrypt(String plaintext, String publicKeyStr) throws Exception {
        BCECPublicKey publicKeyFromXY = SM2.getPublickeyFromXY(publicKeyStr);
        byte[] encrypt = encrypt(plaintext.getBytes("utf-8"), publicKeyFromXY.getEncoded());
        String enStr = new String(Base64.encodeBase64(encrypt));
        return enStr;
    }

    /**
     * 解密
     * @param data       数据
     * @param privateKey 私钥
     * @return 解密之后的数据
     */
    public static byte[] decrypt(byte[] data, byte[] privateKey) throws Exception {
        CipherParameters privateKeyParameters = SM2.privateKeyToParams("SM2", privateKey);
        SM2Engine engine = new SM2Engine(DIGEST, SM2Engine.Mode.C1C3C2);
        engine.init(false, privateKeyParameters);
        byte[] byteDate = engine.processBlock(data, 0, data.length);
        return byteDate;
    }

    /**
     * 私钥解密
     * @param ciphertext
     * @param privateKeyStr
     * @return
     * @throws Exception
     */
    public static String decrypt(String ciphertext, String privateKeyStr) throws Exception {
        BCECPrivateKey privateKeyFromD = SM2.getPrivatekeyFromD(privateKeyStr);
        String plaintext = new String(decrypt(Base64.decodeBase64(ciphertext), privateKeyFromD.getEncoded()), "utf-8");
        return plaintext;
    }

    /**
     * 签名
     * @param data 数据
     * @return 签名
     */
    public static byte[] sign(byte[] data, byte[] privateKey) throws Exception {
        SM2Signer signer = new SM2Signer();
        CipherParameters param = new ParametersWithRandom(SM2.privateKeyToParams("SM2", privateKey));
        signer.init(true, param);
        signer.update(data, 0, data.length);
        return signer.generateSignature();
    }

    /**
     * 签名
     * @param plaintext
     * @param privateKey
     * @return
     * @throws Exception
     */
    public static String sign(String plaintext, String privateKey) throws Exception {
        BCECPrivateKey privateKeyFromD = SM2.getPrivatekeyFromD(privateKey);
        String signature = new String(Base64.encodeBase64(
                sign(plaintext.getBytes("utf-8"), privateKeyFromD.getEncoded()))
        );
        return signature;
    }

    /**
     * 验签
     * @param data      数据
     * @param sign      签名
     * @param publicKey 公钥
     * @return 是否验证通过
     */
    public static boolean verify(byte[] data, byte[] sign, byte[] publicKey) throws Exception {
        SM2Signer signer = new SM2Signer();
        CipherParameters param = SM2.publicKeyToParams("SM2", publicKey);
        signer.init(false, param);
        signer.update(data, 0, data.length);
        return signer.verifySignature(sign);
    }

    /**
     * 验签
     * @param plaintext
     * @param signature
     * @param publicKeyStr
     * @return
     * @throws Exception
     */
    public static boolean verify(String plaintext, String signature, String publicKeyStr) throws Exception {
        BCECPublicKey publicKeyFromXY = SM2.getPublickeyFromXY(publicKeyStr);
        boolean verify = verify(plaintext.getBytes("utf-8"), Base64.decodeBase64(signature), publicKeyFromXY.getEncoded());
        return verify;
    }

    public static void main(String[] args) throws Exception {
        // 生成SM2公私钥
//        KeyPair keyPair = SM2.generateSm2KeyPair();
//
//        String[] pair = SM2.keyPairToString(keyPair);
//        String publicKeyStr = pair[0];
//        String privateKeyStr = pair[1];
//        System.out.println("公钥："+publicKeyStr);
//        System.out.println("私钥："+privateKeyStr);
//
//        //明文
//        String plaintext = "123456789";
//
//        // 加密
//        String ciphertext = encrypt(plaintext, publicKeyStr);
//        System.out.println("加密密文: " + ciphertext);
//
//        // 解密
//        plaintext = decrypt(ciphertext, privateKeyStr);
//        System.out.println("解密明文: " + plaintext);
//
//        // 签名
//        String signature = sign(plaintext, privateKeyStr);
//        System.out.println("signature: " + signature);
//
//        // 验签
//        boolean result = verify(plaintext, signature, publicKeyStr);
//        System.out.println("verify result: " + result);
//
//        System.out.println("=======================================================");
//        String encodeUrlParams = URLCodeUtil.encodeUrlParams(ciphertext);
//        System.out.println(ciphertext);
//        System.out.println(encodeUrlParams);
//        String decodeUrlParams = URLCodeUtil.decodeUrlParams(encodeUrlParams);
//        System.out.println(decodeUrlParams);
        String str="BF2lX8tj69WjMV1s7+1mmD430CTfWsE8u7nCXyUtugrDLkMpQcAQqxa1AcKoLSp2Ft4oZSq26v+XNUbsexDOU8KWtagwwPCxr8wGCkQBrKDnSOjQena0oJYJPsbky+rWrz+XP9F/vZiUNKjL";
        String privateKey="AJvk7HHx3BnLlANgNohbyC/d6P3ldKa4IAB4H/qMbQ9h";
//        String decodeUrlParams = URLCodeUtil.decodeUrlParams(str);
//        System.out.println(decodeUrlParams);
        String decrypt = SM2Util.decrypt(str, privateKey);
        System.out.println(decrypt);

    }

}
